package socketmvc.server.tcp;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.TioConfig;
import org.tio.core.intf.Packet;
import org.tio.server.intf.TioServerHandler;
import socketmvc.core.filter.CommandDispatcherFilter;
import socketmvc.core.util.PacketHandler;
import socketmvc.core.adapter.SocketComponentAdapter;
import socketmvc.server.context.IServerContext;
import socketmvc.server.context.SocketChannel;
import socketmvc.server.context.ThreadSocketChannelHandler;
import socketmvc.core.exception.ResponseException;
import socketmvc.core.filter.AntPathFilterChain;
import socketmvc.core.packets.ErrorPacket;
import socketmvc.core.packets.IOPacket;
import socketmvc.core.packets.basic.IPacket;

import java.nio.ByteBuffer;

public class TcpMsgHandler implements TioServerHandler {

    private final Logger logger = LoggerFactory.getLogger(TcpMsgHandler.class);

    private final IServerContext serverContext;

    private final PacketHandler packetHandler;

    private final SocketComponentAdapter componentAdapter;

    public TcpMsgHandler(IServerContext serverContext) {
        this.serverContext = serverContext;
        this.componentAdapter = new SocketComponentAdapter(
                serverContext.getMvcConfig().getProcessorMethodHandler(),
                serverContext.getMvcConfig().getProtocolCodecHandler(),
                serverContext.getMvcConfig().getFilterHandler());
        this.packetHandler = new PacketHandler(this.componentAdapter);
    }

    /**
     * 解码：把接收到的ByteBuffer，解码成应用可以识别的业务消息包
     * 总的消息结构：消息头 + 消息体
     * 消息头结构：    4个字节，存储消息体的长度
     * 消息体结构：   对象的json串的byte[]
     */
    @Override
    public IOPacket decode(ByteBuffer buffer, int limit, int position, int readableLength, ChannelContext channelContext) {
        //提醒：buffer的开始位置并不一定是0，应用需要从buffer.position()开始读取数据
        //收到的数据组不了业务包，则返回null以告诉框架数据不够
        if (readableLength < 1){
            return null;
        }
        // 解析调用各种类型的packet ，调用对应的decode
        IPacket iPacket = packetHandler.toPacket(buffer);

        if (iPacket != null){
            return new IOPacket(iPacket);
        }

        return null;
    }

    /**
     * 编码：把业务消息包编码为可以发送的ByteBuffer
     * 总的消息结构：消息头 + 消息体
     * 消息头结构：    4个字节，存储消息体的长度
     * 消息体结构：   对象的json串的byte[]
     */
    @Override
    public ByteBuffer encode(Packet packet, TioConfig tioConfig, ChannelContext channelContext) {
        IOPacket ioPacket = (IOPacket) packet;
        IPacket iPacket = ioPacket.getPacket();
        // 解析调用各种类型的packet ，调用对应的encode
        return packetHandler.toByteBuffer(iPacket);
    }


    /**
     * 处理消息
     */
    @Override
    public void handler(Packet packet, ChannelContext channelContext) {
        IOPacket ioPacket = (IOPacket) packet;
        IPacket imPacket = ioPacket.getPacket();

        SocketChannel context = new SocketChannel(channelContext, "tcp");
        context.setPacket(imPacket);
        context.setServerContext(serverContext);
        ThreadSocketChannelHandler.set(context);
        // 构造过滤器器链条，并开始执行链条内的任务
        AntPathFilterChain filterChain = new AntPathFilterChain(componentAdapter.getFilters(context,imPacket));
        try {
            filterChain.doFilter(context,null);
        }catch (ResponseException re){
            printErr(re);
            context.send(new ErrorPacket(re.getResponseMsg(),imPacket.getResponseKey()));
        } catch (Exception e){
            printErr(e);
            context.send(new ErrorPacket(e,imPacket));
        }finally {
            ThreadSocketChannelHandler.remove();
        }
    }


    /**
     * 打印异常
     *
     * @param e ex
     */
    private void printErr(Exception e) {
        if (logger.isErrorEnabled()) {
            logger.error("An exception occurred in the filter chain ===>", e);
        } else {
            System.err.println("An exception occurred in the filter chain ===>");
            e.printStackTrace(System.err);
        }
    }
}
